﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Diagnostics;
using System.Windows.Forms;

namespace UltraSystemLib
{
    public class ErrorLog
    {
        private ConcurrentQueue<string> errorLog;
        private static ErrorLog log;
        public int Count
        {
            get
            {
                return errorLog.Count;
            }
        }
        public static ErrorLog Instance()
        {
            if (log == null)
            {
                log = new ErrorLog();
            }
            return log;
        }
		private ErrorLog()
        {
            errorLog = new ConcurrentQueue <string> ();
        }
        ~ErrorLog()
        {
            Clear();
        }
        public void WriteMesssage(string errMsg)
        {
            if (errorLog.Count > 1000)
            {
                return;
            }
            errorLog.Enqueue(DateTime.Now.ToString() + ' ' + errMsg + Environment.NewLine);
        }
        public string ReadMessage()
        {
            string outString;
            errorLog.TryDequeue(out outString);
            return outString;
        }
        public void Clear()
        {
            while (!errorLog.IsEmpty)
            {
                string outString;
                errorLog.TryDequeue(out outString);
            }
        }
    }
    public class UltraXMLConfigReader : IDisposable
    {
        private XmlReaderSettings settings;
        private XmlReader reader;
        public UltraXMLConfigReader(string xmlFile)
        {
            settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Document;
            settings.IgnoreWhitespace = true;
            settings.IgnoreComments = true;
            reader = XmlReader.Create(xmlFile, settings);
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (IsOpened)
                    {
                        reader.Close();
                    }
                }
                disposed = true;
            }
        }
        public bool IsOpened
        {
            get
            {
                return reader != null;
            }
        }
        public Dictionary<string, Dictionary<string, object>> ReadGroup(string group)
        {
            return ReadGroup(group, null);
        }
        public Dictionary<string, Dictionary<string, object>> ReadGroup(string group, string description)
        {
            var groupParaments = new ArrayList();
            var nodeStart = false;
            var config = new Dictionary<string, Dictionary<string, object>>();
            Dictionary<string, object> fragment = null;
            bool groupFound = false;
            string nodeName = string.Empty;
            string elementName = string.Empty;
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Attribute:
                        break;
                    case XmlNodeType.CDATA:
                        break;
                    case XmlNodeType.Comment:
                        break;
                    case XmlNodeType.Document:
                        break;
                    case XmlNodeType.DocumentFragment:
                        break;
                    case XmlNodeType.DocumentType:
                        break;
                    case XmlNodeType.Element:
                        if (groupFound)
                        {
                            elementName = reader.LocalName;
                            fragment.Add(elementName, null);
                        }
                        else if (nodeStart)
                        {
                            nodeName = reader.LocalName;
                            groupFound = true;
                            fragment = new Dictionary<string, object>();
                            config.Add(nodeName, fragment);
                            if (reader.HasAttributes)
                            {
                                while (reader.MoveToNextAttribute())
                                {
                                }
                            }
                        }
                        else
                        {
                            if (reader.Name.Equals(group))
                            {
                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        if (description == null || reader.Value.Equals(description))
                                        {
                                            nodeStart = true;
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    nodeStart = true;
                                }
                            }
                        }
                        break;
                    case XmlNodeType.EndElement:
                        if (reader.Name.Equals(group))
                        {
                            return config;
                        }
                        else if (reader.Name.Equals(nodeName))
                        {
                            groupFound = false;
                        }
                        break;
                    case XmlNodeType.EndEntity:
                        break;
                    case XmlNodeType.Entity:
                        break;
                    case XmlNodeType.EntityReference:
                        break;
                    case XmlNodeType.None:
                        break;
                    case XmlNodeType.Notation:
                        break;
                    case XmlNodeType.ProcessingInstruction:
                        break;
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    case XmlNodeType.Text:
                        if (nodeStart)
                        {
                            fragment[elementName] = reader.Value;
                        }
                        break;
                    case XmlNodeType.Whitespace:
                        break;
                    case XmlNodeType.XmlDeclaration:
                        break;
                    default:
                        break;
                }
            }
            return null;
        }
        public Dictionary<string, Dictionary<string, Dictionary<string, object>>> ReadAll()
        {
            var config = new Dictionary<string, Dictionary<string, Dictionary<string, object>>>();
            string nodeName = string.Empty;
            string fragmentName = string.Empty;
            string elementName = string.Empty;
            string itemName = string.Empty;
            int degree = 0;
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Attribute:
                        break;
                    case XmlNodeType.CDATA:
                        break;
                    case XmlNodeType.Comment:
                        break;
                    case XmlNodeType.Document:
                        break;
                    case XmlNodeType.DocumentFragment:
                        break;
                    case XmlNodeType.DocumentType:
                        break;
                    case XmlNodeType.Element:
                        if (reader.Name.Equals("root"))
                        {
                            continue;
                        }
                        if (degree == 0)
	                    {
                            fragmentName = reader.LocalName;
                            reader.MoveToNextAttribute();
                            nodeName = reader.Value;
                            config.Add(reader.Value, new Dictionary<string, Dictionary<string, object>>());
                            degree = 1;
	                    }
                        else if (degree == 1)
                        {
                            elementName = reader.LocalName;
                            config[nodeName].Add(reader.LocalName, new Dictionary<string, object>());
                            degree = 2;
                        }
                        else
                        {
                            itemName = reader.LocalName;
                        }
                        break;
                    case XmlNodeType.EndElement:
                        if (reader.LocalName.Equals(fragmentName))
                        {
                            degree = 0;
                        }
                        else if (reader.LocalName.Equals(elementName))
                        {
                            degree = 1;
                        }
                        break;
                    case XmlNodeType.EndEntity:
                        break;
                    case XmlNodeType.Entity:
                        break;
                    case XmlNodeType.EntityReference:
                        break;
                    case XmlNodeType.None:
                        break;
                    case XmlNodeType.Notation:
                        break;
                    case XmlNodeType.ProcessingInstruction:
                        break;
                    case XmlNodeType.SignificantWhitespace:
                        break;
                    case XmlNodeType.Text:
                        if (degree == 2)
                        {
                            config[nodeName][elementName][itemName] = reader.Value;
                        }
                        break;
                    case XmlNodeType.Whitespace:
                        break;
                    case XmlNodeType.XmlDeclaration:
                        break;
                    default:
                        break;
                }
            }
            return config;
        }
    }
    public class UltraXMLConfigWriter : IDisposable
    {
        private XmlWriter writer;
        private XmlWriterSettings settings;
        string urn;
        public UltraXMLConfigWriter(string path, string xmlConfigFile, string configComment)
        {
            try
            {
                settings = new XmlWriterSettings();
                settings.Encoding = Encoding.UTF8;
                settings.ConformanceLevel = ConformanceLevel.Document;
                settings.CloseOutput = true;
                settings.Indent = true;
                settings.IndentChars = ("\t");
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                writer = XmlWriter.Create(path + '\\' + xmlConfigFile, settings);
                writer.WriteStartDocument();
                if (configComment != null)
                {
                    writer.WriteComment(configComment);
                }
            }
            catch (Exception e)
            {
                writer = null;
                ErrorLog.Instance().WriteMesssage(e.Message);
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (IsCreated)
                    {
                        writer.Flush();
                        writer.Close();
                    }
                }
                disposed = true;
            }
        }
        public void WriteDeviceDescription(string systemName, string device, string appType)
        {
            try
            {
                urn = "sys:" + systemName;
                writer.WriteStartElement("root");
                writer.WriteAttributeString("system", systemName);
                writer.WriteAttributeString("device", device);
                writer.WriteAttributeString("appType", appType);
            }
            catch (System.Exception)
            {

            }
        }
        public void WriteEndRootElement()
        {
            writer.WriteEndElement();
        }
        public void WriteElement(string element, string content)
        {
            writer.WriteStartElement(element);
            writer.WriteString(content);
            writer.WriteEndElement();
        }
        public void WriteStartGroup(string group, string content)
        {
            writer.WriteStartElement(group);
            writer.WriteAttributeString("xmlns", "device", null, content);
        }
        public void WriteEndGroup()
        {
            writer.WriteEndElement();
        }
        public void WriteConfigFragment(string comment, string[] fragment, object[] items)
        {
            try
            {

                if (comment != null)
                {
                    writer.WriteComment(comment);
                }
                writer.WriteStartElement(fragment[0]);

                // Write the namespace declaration.
                writer.WriteAttributeString("xmlns", fragment[1], null, fragment[2]);
                for (int i = 0; i < items.Length; i += 2)
                {
                    writer.WriteElementString(items[i].ToString(), fragment[2], items[i + 1].ToString());
                }
                writer.WriteEndElement();
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public void WriteComment(string comment)
        {
            try
            {
                writer.WriteComment(comment);
            }
            catch (System.Exception)
            {

            }
        }
        public bool IsCreated
        {
            get
            {
                return writer != null;
            }
        }
    }
    public class UltraXMLDocument : IDisposable
    {
        private XDocument xDocument;
        private string xmlFilePath;
        public XElement RootElement
        {
            get
            {
                try 
	            {
                    return xDocument.Element("Root");
	            }
	            catch (Exception e)
	            {
                    ErrorLog.Instance().WriteMesssage(e.Message);
		            return null;
	            }
            }
        }
        public bool IsNew { set; get; }
        public XDocument Document
        {
            get
            {
                return xDocument;
            }
        }
        public UltraXMLDocument(){}
        public bool Create(string pathName, string description)
        {
            var offset = pathName.LastIndexOf(@"\");
            if (offset >= 0)
            {
                var path = pathName.Substring(0, offset);
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            if (File.Exists(pathName))
            {
                File.Delete(pathName);
            }

            xmlFilePath = pathName;
            try
            {
                
                xDocument = new XDocument();
                IsNew = true;
                CreateRootElement(description, null);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        public UltraXMLDocument(string path, string file)
        {
            path = Path.GetFullPath(path);
            xmlFilePath = path + "\\" + file;
            if (File.Exists(xmlFilePath))
            {
                try
                {
                    xDocument = XDocument.Load(xmlFilePath);
                    IsNew = false;
                }
                catch (Exception e)
                {
                    ErrorLog.Instance().WriteMesssage("Load xml file error: " + e.Message);
                }
            }
            else
            {
                try
                {
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                    }
                    xDocument = new XDocument();
                    IsNew = true;
                    CreateRootElement("UltraService config", null);
                }
                catch (Exception e)
                {
                    ErrorLog.Instance().WriteMesssage("Create xml file error: " + e.Message);
                }
            }
        }
        public void CreateRootElement(string comment, XAttribute[] attributes)
        {
            xDocument.Add(new XComment(comment), new XElement("Root", attributes));
        }
        public XElement WriteStartFragment(XElement node, string subnodeName, string comment)
        {
            if (comment != null)
            {
                node.Add(new XComment(comment));
            }
            node.Add(new XElement(subnodeName));
            return node.Element(subnodeName);
        }
        public XElement WriteStartGroup(XElement node, string nodeName, string groupName, string comment)
        {
            if (comment != null)
            {
                node.Add(new XComment(comment));
            }
            node.Add(new XElement(groupName));
            return node.Element(groupName);
        }
        public XElement WriteStartGroup(XElement node, string groupName, string comment)
        {
            if (comment != null)
            {
                node.Add(new XComment(comment));
            }
            node.Add(new XElement(groupName));
            return xDocument.Element(groupName);
        }
        public void AddAttribute(XElement element, string attribute, object value)
        {
            element.Add(new XAttribute(attribute, value));
        }
        public void AddItem(XElement element, string itemName, object value)
        {
            var itemText = new XText(value.ToString());
            element.Add(new XElement(itemName, itemText));
        }
        public bool SaveDocument()
        {
            return SaveDocument(xmlFilePath);
        }
        public bool SaveDocument(string filePath)
        {
            try
            {
                xDocument.Save(filePath);
            }
            catch (Exception e)
            {
                ErrorLog.Instance().WriteMesssage("Save xml file error: " + e.Message);
                return false;
            }
            return true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                }
                disposed = true;
            }
        }
    }
    public class ExecuteProcess
    {
        private Process process;
        public ExecuteProcess(string pathName, string args)
        {
            System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo();
            Info.FileName = pathName;
            Info.Arguments = args;
            Info.WindowStyle = ProcessWindowStyle.Hidden;
            try
            {
                process = System.Diagnostics.Process.Start(Info);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
            }
        }
        public ExecuteProcess(string pathName, string args, bool hidden)
        {
            System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo();
            Info.FileName = pathName;
            Info.Arguments = args;
            Info.WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
            try
            {
                process = System.Diagnostics.Process.Start(Info);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
            }
        }
        public void KillProcess()
        {
            try
            {
                process.Kill();
            }
            catch
            {

            }
        }
        public bool HasExited
        {
            get
            {
                return process.HasExited;
            }
        }
    }
    public enum NetEvent
    {
        DataReceived, NetBroken, NetConnecting, NetConnected, NetUnconnected,
        SocketError, SocketClosed, PeerClosed
    }
    public delegate void CommDataReceived(UInt32 id, NetEvent netEvent, object[] paraments);
    public class Comm
    {
        private System.IO.Ports.SerialPort serialPort;
        private CommDataReceived dataHandler;
        public string LastErrorMsg { set; get; }
        public UInt32 ConnectionID { set; get; }
        public Comm(UInt32 id, string portName, int baudRate, CommDataReceived dataHandler)
        {
            ConnectionID = id;
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            serialPort = new System.IO.Ports.SerialPort()
            {
                BaudRate = baudRate,
                PortName = portName,
                Parity = System.IO.Ports.Parity.None,
                StopBits = System.IO.Ports.StopBits.One,
                DataBits = 8,
                DtrEnable = false,
                RtsEnable = false,
                Handshake = System.IO.Ports.Handshake.None,
                ReadBufferSize = 8192,
                ReadTimeout = 300,
                WriteBufferSize = 256,
                WriteTimeout = 300
            };
            serialPort.DataReceived += CommDataReceived;
            this.dataHandler = dataHandler;
        }
        private void CommDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            try
            {
                if (serialPort.BytesToRead == 0)
                {
                    return;
                }
                var buffer = new byte[serialPort.BytesToRead];
                serialPort.Read(buffer, 0, buffer.Length);
                dataHandler(ConnectionID, NetEvent.DataReceived, new object[] { buffer });
            }
            catch (Exception ex)
            {
                LastErrorMsg = ex.Message;
            }
        }
        public bool SendData(byte[] data)
        {
            return SendData(data, 0, data.Length);
        }
        public bool SendData(byte[] data, int offset, int length)
        {
            try
            {
                serialPort.Write(data, offset, length);
                return true;
            }
            catch (Exception ex)
            {
                LastErrorMsg = ex.Message;
                return false;
            }
        }
        public bool SendString(string text)
        {
            try
            {
                serialPort.Write(text);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        public bool OpenPort()
        {
            try
            {
                serialPort.Open();
            }
            catch (Exception ex)
            {
                LastErrorMsg = ex.Message;
            }
            return serialPort.IsOpen;
        }
        public bool ClosePort()
        {
            try
            {
                serialPort.Close();
                serialPort.Dispose();

                return true;
            }
            catch (Exception ex)
            {
                LastErrorMsg = ex.Message;
                return false;
            }
        }
    }
}
